package com.grandtech.insurance.common.core.coding;

import com.grandtech.insurance.common.core.coding.Filter.Any;
import com.grandtech.insurance.common.core.coding.Filter.Single;
import com.grandtech.insurance.common.core.tile.VectorTile;
import com.grandtech.insurance.common.core.tile.VectorTile.Tile;
import com.grandtech.insurance.common.core.tile.VectorTile.Tile.Layer;
import com.grandtech.insurance.common.core.tile.VectorTile.Tile.Value;
import com.vividsolutions.jts.geom.*;

import java.io.IOException;
import java.util.*;

public class VectorTileDecoder {
    private boolean autoScale = true;

    public VectorTileDecoder() {

    }

    public boolean isAutoScale() {
        return this.autoScale;
    }

    public void setAutoScale(boolean autoScale) {
        this.autoScale = autoScale;
    }

    public FeatureIterable decode(byte[] data) throws IOException {
        return this.decode(data, Filter.ALL);
    }

    public FeatureIterable decode(byte[] data, String layerName) throws IOException {
        return this.decode(data, (new Single(layerName)));
    }

    public FeatureIterable decode(byte[] data, Set<String> layerNames) throws IOException {
        return this.decode(data, (new Any(layerNames)));
    }

    public FeatureIterable decode(byte[] data, Filter filter) throws IOException {
        VectorTile.Tile tile = VectorTile.Tile.parseFrom(data);
        return new FeatureIterable(tile, filter, this.autoScale);
    }

    static int zigZagDecode(int n) {
        return n >> 1 ^ -(n & 1);
    }

    public static final class Feature {
        private final String layerName;
        private final int extent;
        private final Geometry geometry;
        private final Map<String, Object> attributes;

        public Feature(String layerName, int extent, Geometry geometry, Map<String, Object> attributes) {
            this.layerName = layerName;
            this.extent = extent;
            this.geometry = geometry;
            this.attributes = attributes;
        }

        public String getLayerName() {
            return this.layerName;
        }

        public int getExtent() {
            return this.extent;
        }

        public Geometry getGeometry() {
            return this.geometry;
        }

        public Map<String, Object> getAttributes() {
            return this.attributes;
        }
    }

    private static final class FeatureIterator implements Iterator<Feature> {
        private final GeometryFactory gf = new GeometryFactory();
        private final Filter filter;
        private final Iterator<Layer> layerIterator;
        private Iterator<Tile.Feature> featureIterator;
        private int extent;
        private String layerName;
        private double scale;
        private boolean autoScale;
        private final List<String> keys = new ArrayList();
        private final List<Object> values = new ArrayList();
        private Feature next;

        public FeatureIterator(Tile tile, Filter filter, boolean autoScale) {
            this.layerIterator = Arrays.asList(tile.layers).iterator();
            this.filter = filter;
            this.autoScale = autoScale;
        }

        public boolean hasNext() {
            this.findNext();
            return this.next != null;
        }

        public Feature next() {
            this.findNext();
            if (this.next == null) {
                throw new NoSuchElementException();
            } else {
                Feature n = this.next;
                this.next = null;
                return n;
            }
        }

        private void findNext() {
            if (this.next == null) {
                while (true) {
                    if (this.featureIterator != null && this.featureIterator.hasNext()) {
                        this.next = this.parseFeature((VectorTile.Tile.Feature) this.featureIterator.next());
                        break;
                    }

                    if (!this.layerIterator.hasNext()) {
                        this.next = null;
                        break;
                    }

                    Layer layer = (Layer) this.layerIterator.next();
                    if (this.filter.include(layer.name)) {
                        this.parseLayer(layer);
                    }
                }

            }
        }

        private void parseLayer(Layer layer) {
            this.layerName = layer.name;
            this.extent = layer.getExtent();
            this.scale = this.autoScale ? (double) this.extent / 256.0D : 1.0D;
            this.keys.clear();
            this.keys.addAll(Arrays.asList(layer.keys));
            this.values.clear();
            Value[] var2 = layer.values;
            int var3 = var2.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                Value value = var2[var4];
                if (value.hasBoolValue()) {
                    this.values.add(value.getBoolValue());
                } else if (value.hasDoubleValue()) {
                    this.values.add(value.getDoubleValue());
                } else if (value.hasFloatValue()) {
                    this.values.add(value.getFloatValue());
                } else if (value.hasIntValue()) {
                    this.values.add(value.getIntValue());
                } else if (value.hasSintValue()) {
                    this.values.add(value.getSintValue());
                } else if (value.hasUintValue()) {
                    this.values.add(value.getUintValue());
                } else if (value.hasStringValue()) {
                    this.values.add(value.getStringValue());
                } else {
                    this.values.add((Object) null);
                }
            }

            this.featureIterator = Arrays.asList(layer.features).iterator();
        }

        private Feature parseFeature(VectorTile.Tile.Feature feature) {
            int tagsCount = feature.tags.length;
            Map<String, Object> attributes = new HashMap(tagsCount / 2);
            int tagIdx = 0;

            while (tagIdx < feature.tags.length) {
                String key = (String) this.keys.get(feature.tags[tagIdx++]);
                Object value = this.values.get(feature.tags[tagIdx++]);
                attributes.put(key, value);
            }

            int x = 0;
            int y = 0;
            List<List<Coordinate>> coordsList = new ArrayList();
            List<Coordinate> coords = null;
            int geometryCount = feature.geometry.length;
            int length = 0;
            int command = 0;
            int i = 0;

            while (i < geometryCount) {
                if (length <= 0) {
                    length = feature.geometry[i++];
                    command = length & 7;
                    length >>= 3;
                }

                if (length > 0) {
                    if (command == 1) {
                        coords = new ArrayList();
                        coordsList.add(coords);
                    }

                    if (command == 7) {
                        if (feature.getType() != 1 && !coords.isEmpty()) {
                            coords.add(coords.get(0));
                        }

                        --length;
                    } else {
                        int dx = feature.geometry[i++];
                        int dy = feature.geometry[i++];
                        --length;
                        dx = VectorTileDecoder.zigZagDecode(dx);
                        dy = VectorTileDecoder.zigZagDecode(dy);
                        x += dx;
                        y += dy;
                        Coordinate coord = new Coordinate((double) x / this.scale, (double) y / this.scale);
                        coords.add(coord);
                    }
                }
            }

            Geometry geometry = null;
            switch (feature.getType()) {
                case 0:
                default:
                    break;
                case 1:
                    List<Coordinate> allCoords = new ArrayList();
                    Iterator var26 = coordsList.iterator();

                    while (var26.hasNext()) {
                        List<Coordinate> cs = (List) var26.next();
                        allCoords.addAll(cs);
                    }

                    if (allCoords.size() == 1) {
                        geometry = this.gf.createPoint((Coordinate) allCoords.get(0));
                    } else if (allCoords.size() > 1) {
                        geometry = this.gf.createMultiPoint((Coordinate[]) allCoords.toArray(new Coordinate[allCoords.size()]));
                    }
                    break;
                case 2:
                    List<LineString> lineStrings = new ArrayList();
                    Iterator var23 = coordsList.iterator();

                    while (var23.hasNext()) {
                        List<Coordinate> cs = (List) var23.next();
                        lineStrings.add(this.gf.createLineString((Coordinate[]) cs.toArray(new Coordinate[cs.size()])));
                    }

                    if (lineStrings.size() == 1) {
                        geometry = (Geometry) lineStrings.get(0);
                    } else if (lineStrings.size() > 1) {
                        geometry = this.gf.createMultiLineString((LineString[]) lineStrings.toArray(new LineString[lineStrings.size()]));
                    }
                    break;
                case 3:
                    List<LinearRing> rings = new ArrayList();
                    Iterator var17 = coordsList.iterator();
                    List<Coordinate> cs;
                    while (var17.hasNext()) {
                        cs = (List) var17.next();
                        //java.lang.IllegalArgumentException: Invalid number of points in LinearRing (found 3 - must be 0 or >= 4)
                        rings.add(this.gf.createLinearRing((Coordinate[]) cs.toArray(new Coordinate[cs.size()])));
                    }

                    if (rings.size() > 0) {
                        LinearRing shell = (LinearRing) rings.get(0);
                        LinearRing[] holes = (LinearRing[]) rings.subList(1, rings.size()).toArray(new LinearRing[rings.size() - 1]);
                        geometry = this.gf.createPolygon(shell, holes);
                    }
            }

            if (geometry == null) {
                geometry = this.gf.createGeometryCollection(new Geometry[0]);
            }

            return new Feature(this.layerName, this.extent, (Geometry) geometry, Collections.unmodifiableMap(attributes));
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    public static final class FeatureIterable implements Iterable<Feature> {
        private final Tile tile;
        private final Filter filter;
        private boolean autoScale;

        public FeatureIterable(Tile tile, Filter filter, boolean autoScale) {
            this.tile = tile;
            this.filter = filter;
            this.autoScale = autoScale;
        }

        public Iterator<Feature> iterator() {
            return new FeatureIterator(this.tile, this.filter, this.autoScale);
        }

        public List<Feature> asList() {
            List<Feature> features = new ArrayList();
            Iterator var2 = this.iterator();

            while (var2.hasNext()) {
                Feature feature = (Feature) var2.next();
                features.add(feature);
            }

            return features;
        }

        public Collection<String> getLayerNames() {
            Set<String> layerNames = new HashSet();
            Layer[] var2 = this.tile.layers;
            int var3 = var2.length;

            for (int var4 = 0; var4 < var3; ++var4) {
                Layer layer = var2[var4];
                layerNames.add(layer.name);
            }

            return Collections.unmodifiableSet(layerNames);
        }
    }
}
